/* @flow */

import type Router from '../index'
import {assert} from './warn'
import {getStateKey, setStateKey} from './push-state'

const positionStore = Object.create(null)

export function setupScroll() {
    // Fix for #1585 for Firefox
    //@todo 如何fix的？
    //replaceState方法的第一个参数是状态对象，也就是popstate事件对象的state属性的值
    window.history.replaceState({key: getStateKey()}, '')
    window.addEventListener('popstate', e => {
        //注意，保存的是跳转之前的那个页面的滚动的位置
        //示例：
        //比如是从'/a'跳转到'/b'，则saveScrollPosition方法是保存'/a'链接下的
        //页面位置，然后将'/b'链接对应的key值作为当前key值，这样就可以根据该key值获得该页面的
        //之前保存的位置信息。
        saveScrollPosition()
        //如果跳转之后的页面的key值存在，则将该key值置为当前key值。
        if (e.state && e.state.key) {
            setStateKey(e.state.key)
        }
    })
}

export function handleScroll(router: Router,
                             to: Route,
                             from: Route,
                             isPop: boolean) {
    if (!router.app) {
        return
    }

    const behavior = router.options.scrollBehavior
    if (!behavior) {
        return
    }

    if (process.env.NODE_ENV !== 'production') {
        assert(typeof behavior === 'function', `scrollBehavior must be a function`)
    }

    // wait until re-render finishes before scrolling
    router.app.$nextTick(() => {
        const position = getScrollPosition()
        const shouldScroll = behavior.call(router, to, from, isPop ? position : null)

        if (!shouldScroll) {
            return
        }

        if (typeof shouldScroll.then === 'function') {
            shouldScroll.then(shouldScroll => {
                scrollToPosition((shouldScroll: any), position)
            }).catch(err => {
                if (process.env.NODE_ENV !== 'production') {
                    assert(false, err.toString())
                }
            })
        } else {
            scrollToPosition(shouldScroll, position)
        }
    })
}

//
export function saveScrollPosition() {
    const key = getStateKey()
    if (key) {
        //一个key是与一个逻辑上的页面（也可以是与一个url）相关联的，用于保存在离开该页面时
        //当前页面的位置，这样当回退到该页面时，可以再次回到该位置
        positionStore[key] = {
            x: window.pageXOffset,
            y: window.pageYOffset
        }
    }
}

function getScrollPosition(): ?Object {
    const key = getStateKey()
    if (key) {
        return positionStore[key]
    }
}

function getElementPosition(el: Element, offset: Object): Object {
    const docEl: any = document.documentElement
    const docRect = docEl.getBoundingClientRect()
    const elRect = el.getBoundingClientRect()
    return {
        x: elRect.left - docRect.left - offset.x,
        y: elRect.top - docRect.top - offset.y
    }
}

function isValidPosition(obj: Object): boolean {
    return isNumber(obj.x) || isNumber(obj.y)
}

function normalizePosition(obj: Object): Object {
    return {
        x: isNumber(obj.x) ? obj.x : window.pageXOffset,
        y: isNumber(obj.y) ? obj.y : window.pageYOffset
    }
}

function normalizeOffset(obj: Object): Object {
    return {
        x: isNumber(obj.x) ? obj.x : 0,
        y: isNumber(obj.y) ? obj.y : 0
    }
}

function isNumber(v: any): boolean {
    return typeof v === 'number'
}

function scrollToPosition(shouldScroll, position) {
    const isObject = typeof shouldScroll === 'object'
    if (isObject && typeof shouldScroll.selector === 'string') {
        const el = document.querySelector(shouldScroll.selector)
        if (el) {
            let offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}
            offset = normalizeOffset(offset)
            position = getElementPosition(el, offset)
        } else if (isValidPosition(shouldScroll)) {
            position = normalizePosition(shouldScroll)
        }
    } else if (isObject && isValidPosition(shouldScroll)) {
        position = normalizePosition(shouldScroll)
    }

    if (position) {
        window.scrollTo(position.x, position.y)
    }
}
